package com.demo.gateway.sentinel.filter;

import com.demo.gateway.sentinel.config.SysConstatnt;
import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.*;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 验证filter，用于处理获取http请求协议中所有内容
 */
@Slf4j
@Configuration
public class ValidataFilter  implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        log.info("{}可以对参数进行验证",this);
        log.info("请求地址：{},method:{},uri:{},header:{},cookies:{},param:{}",request.getPath(),request.getMethod(),request.getURI(),request.getHeaders().toSingleValueMap(),request.getCookies(),request.getQueryParams());
        //exchange.getAttribute(org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_PATH_CONTAINER_ATTR);
        org.springframework.cloud.gateway.route.Route route = exchange.getAttribute(org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        // 负载信息
        log.info("请求目标负载均衡信息：{}",route.getUri());
        // 目标示例信息
        log.info("请求目标示例元数据信息：{}",route.getMetadata());
        // 请求参数
        log.info("请求参数-查询字符串部分：{}",request.getQueryParams());
        // 建议基于context-type进行分别处理
        if(request.getHeaders().getContentType() !=null && request.getHeaders().getContentType().includes(MediaType.MULTIPART_FORM_DATA)) {
            // 文件流的处理建议慎重
            log.info("请求中包含提交的文件内容");
        }

        // 获取请求体数据
        //当body中没有缓存时，只会执行这一个拦截器， 原因是fileMap中的代码没有执行，所以需要在body为空时构建一个空的缓存
        //构建新数据流， 当body为空时，构建空流
        return DataBufferUtils.join(exchange.getRequest().getBody().defaultIfEmpty(new DefaultDataBufferFactory().allocateBuffer(0)))
                .flatMap(dataBuffer -> {
                    //  增加应用计数，可多次读取，针对body二次封装的方法二，方法一切勿加入这一行
                    DataBufferUtils.retain(dataBuffer);
                    int dataLength = dataBuffer.readableByteCount();
                    // todo 读取body是有风险的，要防止OOM
                    byte[] bytes = new byte[dataLength];
                    dataBuffer.read(bytes);
                    DataBufferUtils.release(dataBuffer);
                    // 编码应结合charset设置，同时需要默认值
                    String bodyStr = new String(bytes, StandardCharsets.UTF_8);
                    // 对请求体进行处理
                    log.info("请求体内容：{}",bodyStr);
                    // todo 执行body相关内容

                    // 将body数据进行二次封装，继续执行过滤器链
                    // 方法一 注意屏蔽掉 DataBufferUtils.retain(dataBuffer);
                  /*  ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(request.getURI()).build();
                    // 与解密编码对应 构建body
                    byte[] bytes1 = bodyStr.getBytes(StandardCharsets.UTF_8);
                    NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
                    DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes1.length);
                    buffer.write(bytes1);
                    Flux<DataBuffer> bodyFlux = Flux.just(buffer);*/
                    // 方法二
                    Flux<DataBuffer> bodyFlux = Flux.just(dataBuffer.slice(0, dataLength));
                    // 请求转出去
                    return chain.filter(exchange.mutate().request(new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return bodyFlux;
                        }
                    }).build());
                });
    }

    @Override
    public int getOrder() {
        return SysConstatnt.FilterOrder.FILTER_VALIDATE;
    }
}